package fcnet

import (
	"reflect"
	"time"

	"github.com/vmihailenco/msgpack/v5"

	"gitee.com/fcsvr/fancy/fclog"
	"gitee.com/fcsvr/fancy/utils"
)

type MsgHandle struct {
	Apis           map[uint32]RouterItf
	WorkerPoolSize uint32
	TaskQueue      []chan *Req

	BenchTm float64
	BenchCt int
}

func NewMsgHandle() *MsgHandle {
	m := &MsgHandle{
		Apis:           make(map[uint32]RouterItf),
		WorkerPoolSize: utils.G_Data.WorkerPoolSize,
		TaskQueue:      make([]chan *Req, utils.G_Data.WorkerPoolSize),
	}
	return m
}

func (this *MsgHandle) DoMsgHandle(req *Req) {
	startTm := time.Now()
	handle, ok := this.Apis[req.GetMsgID()]
	if !ok {
		fclog.Error("MsgHandle DoMsgHandle: api msgid = %d is not exist", req.GetMsgID())
		return
	}

	argTyp := reflect.TypeOf(handle.GetArg())
	fclog.Info("MsgHandle DoMsgHandle: msg argTyp = %+v", argTyp)

	argvi := reflect.New(argTyp.Elem()).Interface()
	if argTyp.Kind() != reflect.Ptr {
		argvi = reflect.New(argTyp).Elem().Addr().Interface()
	}
	fclog.Info("MsgHandle DoMsgHandle: msg argv = %+v", argvi)

	//if err := json.Unmarshal(req.GetData(), argvi); err != nil {
	if err := msgpack.Unmarshal(req.GetData(), argvi); err != nil {
		panic(err)
	}

	fclog.Info("MsgHandle DoMsgHandle: msg argTyp = %+v, argv = %+v", argTyp, argvi)
	handle.SetArg(argvi)

	handle.PreHandle(req)
	handle.Handle(req)
	handle.PostHandle(req)
	this.BenchTm += time.Since(startTm).Seconds()
	this.BenchCt++
	fclog.Warn("MsgHandle DoMsgHandle: average = %d, benchtm = %f", this.BenchCt, this.BenchTm/float64(this.BenchCt))
}

func (this *MsgHandle) AddRouter(msgid uint32, router RouterItf) {
	if _, ok := this.Apis[msgid]; ok {
		panic("repeated api, msgid")
	}
	this.Apis[msgid] = router
	fclog.Info("MsgHandle AddRouter: msgid = %d", msgid)
}

func (this *MsgHandle) StartOneWorker(workerid int, taskqueue chan *Req) {
	fclog.Info("MsgHandle StartOneWorker: worker id = %d", workerid)
	for {
		select {
		case req := <-taskqueue:
			this.DoMsgHandle(req)
		}
	}
}

func (this *MsgHandle) StartWorkerPool() {
	for i := 0; i < int(this.WorkerPoolSize); i++ {
		workid := i
		this.TaskQueue[workid] = make(chan *Req, utils.G_Data.WorkerTaskLen)
		go this.StartOneWorker(workid, this.TaskQueue[i])
	}
}

func (this *MsgHandle) SendMsgToTaskQueue(req *Req) {
	workerid := req.GetConn().GetConnID() % this.WorkerPoolSize
	this.TaskQueue[workerid] <- req
}
